import com.alibaba.fastjson.JSON;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.Options;
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.AbstractVariadicFunction;
import com.googlecode.aviator.runtime.type.*;
import lombok.extern.slf4j.Slf4j;
import wei.yigulu.modbus.domain.datatype.ModbusDataTypeEnum;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


@Slf4j
public class PointDataCalculator extends BasePointData {



    private static final Pattern RANDOM_EXPR = Pattern.compile("R\\((\\d+)?\\)");

    private static final Pattern TIME_EXPR = Pattern.compile("T\\(\\)");


    static{
        AviatorEvaluator.setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
        AviatorEvaluator.addFunction(new PFunction());
    }

    /**
     * 计算表达式
     */
    Expression compiledExp;

    public PointDataCalculator(Integer point, ModbusDataTypeEnum dataType, String formula) {
        super(point, dataType, formula);
        getOrInitCompiledExp();
    }

    public Object calculate() {
        Object o = null;
        try {
            o = getOrInitCompiledExp().execute();
        } catch (ArithmeticException e) {
            log.error("除数为0,计算公式为：{},请检查数据池内是否有该点位是否有数据", getValue());
        } catch (Exception e) {
            log.error("数据计算{}时发生异常", getValue(), e);
            throw new RuntimeException("计算器计算数据时发生异常,公式为：" + getValue());
        }
        return o;
    }


    /**
     * 对公式进行初始化  如果公式解析失败将会抛出异常
     *
     * @return Expression  解析完成的表达式
     */
    private Expression getOrInitCompiledExp() {
        if (this.compiledExp == null) {
            try {
                String temS;
                String temS2;
                String temFormula = getValue();
                Matcher m = RANDOM_EXPR.matcher(getValue());
                while (m.find()) {
                    temS = m.group();
                    temS2 = temS.replace("R", "rand");
                    temFormula = temFormula.replace(temS, temS2);
                }
                m = TIME_EXPR.matcher(getValue());
                while (m.find()) {
                    temS = m.group();
                    temS2 = temS.replace("T", "now");
                    temFormula = temFormula.replace(temS, temS2);
                }
                this.compiledExp = AviatorEvaluator.compile(temFormula);
            } catch (ExpressionSyntaxErrorException e) {
                log.error("公式解析失败，公式不可用", e);
                throw new RuntimeException("公式解析失败，公式不可用");
            }
        }
        return this.compiledExp;
    }




    @Override
    public void joinRealTimeData() {
        protocolDataContainer.addCalculator(this);
    }


    public static class PFunction extends AbstractFunction {


        private static final long serialVersionUID = -7377110880312266008L;

        @Override
        public String getName() {
            return "P";
        }

        public AviatorObject call(Map<String, Object> env, AviatorObject arg) {
            if(AviatorType.Long==arg.getAviatorType()){
               return AviatorRuntimeJavaType.valueOf(ProtocolDataContainer.getInstance().getData(((AviatorLong)arg).toBigInt().intValue()));
            }
            return AviatorRuntimeJavaType.valueOf(null);

        }

    }
}
